﻿// Edk lib
// Copyright (c)2010 Frank Cheung <frank@ajaxjs.com>
// 本代码可以免费使用、修改，希望我的程序能为您的工作带来方便。同时请保留以上请息。

﻿$$ = {
	newInstance: function(_prototype){
        var dummy = new Function;
        dummy.prototype = _prototype.prototype;
        return new dummy;
    }
}

$$.apply = Object.apply = function(a, b){for(var i in b)a[i] = b[i];return a;}

/**
 * 
function base(a1, a2){
	this.a1 = "init...base class, the value is " + a1;
	this.a2 = a2;
}
base.prototype = {
	sayHi:function (){
		this.sayingHi = true;
		return "Hello";
	}
};

function sub(a3, a4){
	//base.apply(this, [1]);
	this.a2 = 2;	
	this.a3 = 'init...sub class, the value is ' + a3;
	this.a4 = a4;
}

sub.prototype.sayHi = function(){
	var hi = base.prototype.sayHi.call(this) + " World"
	return hi;	
}

sub.print = function(_instance){
	return _instance.saiHi();
}


function sub2(){}
sub2.prototype.sayHi = function(){
    return this._sayHi() + ' Man!';
}

_extend(base, sub);
_extend(sub, sub2);
foo = new sub2
alert(foo.sayHi())
//alert(foo instanceof base);
// 1 解决构造器的问题，2 解决了构造器的问题，却影响原型链
// <--用dummy一石二鸟解决以上两个问题！
 */
Object.extend = function(base, sub){
    var dummy = function(){}
    dummy.prototype = base.prototype; // key step!
    var superObject = new dummy;
    var subProto = sub.prototype;
    // sub.prototype = new base() // this will call const. no good
    // sure, this will not, and still in the same prototype chain!
    sub.prototype = superObject;
    sub.prototype.constructor = base;
    
    for(var i in subProto){
        if(i in sub.prototype){
            sub.prototype["_" + i] = sub.prototype[i];
            sub.prototype[i]       = subProto[i];
        }else{
            sub.prototype[i] = subProto[i];
        }
    }
    return sub;
}

Object.mix = function(_class, mix){
	var 
		 classProto	= _class.prototype
		,mixProto	= mix.prototype;
	
	for(var i in mixProto){
		if(i in classProto){
			classProto["_" + i] = mixProto[i];
		}else{
			classProto[i] = mixProto[i];
		}
	}
}
/**
 * 输入参数，还原它的primitive值。
 * @param {Any} v
 * @return {Any}
 */
Object.getPerm = function(v){
	if(v){
		if(v == 'true' ) return true;
		if(v == 'false') return false;
		if(v.toString() == (new Date(v)).toString()) return new Date(v);
      	if(v == Number(v)) return Number(v);
	}
  	return v;
}

/**
 * 
 * <code>var s = {
    toString : function(){return 's';}
};
move(s, 's.b8b.yuy.kj', 1);
move(s, 's.b8b', 1);
move(s, 's.zzz', 0);
 </ocde>
alert(s.b7b)
 * 不返回结果，只是在对象身上处理。
 * @param {Object} container
 * @param {String} _namespaces
 * @param {Boolean} isOvrToString
 */
$$.move = function (container, _namespaces, isOvrToString){
    var 
         moveAt
        ,_namespaces = _namespaces.split(".")
        ,moveStrAt
        ,newSpace;
    
    moveAt = container; // first
    
    if(!_namespaces || !_namespaces.length || _namespaces.length == 1) return null;

    // container断言存在了的，所以从1开始循环……
    for(var i = 1, j = _namespaces.length; i < j; i++){
        moveStrAt = _namespaces[i];
        if(!moveAt[moveStrAt]){
            newSpace = new Object;
            if(isOvrToString){
                // 对象自身的反射，利用局部变量来保存一份引用
                // 例如:
                // defineLiteral('bar', 'baz', 'qux','quux'); // =>
                // bar.baz.qux.quux // bar.baz.qux.quux.toString(); //
                // =>'bar.baz.qux.quux'
                var backStr = [container.toString()];
                for(var k = 1; k <= i; k++){
                    backStr.push(_namespaces[k]);
                }
                backStr = backStr.join('.');
                newSpace.toString = function(){
                    return backStr;
                }
            } 
            moveAt[moveStrAt] = newSpace;   
        }
        // 前一位( moveAt[moveStrAt] = newSpace)已经非undefined了，可以移到下一位。
        moveAt = moveAt[moveStrAt]; 
    }
}

/**
 * 遍历数组，可代替写循环。
 * 如果传入一个非Array类型的参数，也会被自动转换为Array。
 * 函数的返回值只会返回到这里，只强调函数的过程而不是函数的结果。
 * 如果送入的函数被执行后返回false，表示将结束该次循环。
 * @param {Array} arr 要循环的数组。
 * @param {Function} fn 执行的函数。
 * @param {Object} scope 函数的作用域。
 */
$$.each = Array.each = function(arr, fn, scope){
	if(!arr.pop && !arr.shift)arr = [arr];
	for(var i = 0, j = arr.length; i < j; i++){
		if(fn.call(scope || obj.globalScope , arr[i], i, j, arr) === false){
			break;
		}
	}
}

/**
 * 修正arguments关键字不是数组的补丁函数。
 * @return {Array}
 */
$$.fixArgs = Array.fixArgs = function (args){
	var _args = [];
	for(var i = 0, j = args.length; i < j; i++){
		_args[i] = args[i];
	}
	return _args;
}

/**
 * 对象转换为数组。
 * @param {Object} obj
 * @return {Array}
 */
$$.toArray = function(obj){
	var arr = [];
	for(var i in obj){
		arr.push(new Array(i, obj[i]));
	}
	return arr;
}

/**
 * 裁剪一段字符串的前后多余的空格。
 * @return {String} 裁剪后的字符串。
 * @credit Ext
 */
String.trim = (function(){
	var noBlank = /^\s+|\s+$/g;
    return function(str){
        return str.replace(noBlank, $$.emptyStr);
    }
})();

$$.String = String.dummy = new String;

/**
 * 格式化字符串。字符串格式型如：“foo{0}bar{1}……”。
 */
String.prototype.format = function(){
    var 
         args 	 = arguments
        ,getSlot = arguments.callee.getSlot
        ,replFn  = function (m, i){
            return args[i];
        };
    
    if(!getSlot){
        getSlot = arguments.callee.getSlot = /{(\d+)}/g;
    }
    
    return this.replace(getSlot, replFn);
}

/**
 * author: meizz
 * new Date().format("yyyy-MM-dd hh:mm:ss")
 * @param {String} format
 */
Date.prototype.format = function(format){
    var 
    	 $1
	    ,o = {
	         "M+": this.getMonth() + 1 //month
	        ,"d+": this.getDate()    //day
	        ,"h+": this.getHours()   //hour
	        ,"m+": this.getMinutes() //minute
	        ,"s+": this.getSeconds() //second
	        /* quarter */
	        ,"q+": Math.floor((this.getMonth() + 3) / 3)  
	        ,"S" : this.getMilliseconds() //millisecond
	    }
	    ,key ,value;
	    
    if (/(y+)/.test(format)) {
    	 $1		= RegExp.$1
    	,format = format.replace($1, String(this.getFullYear()).substr(4 - $1));
    }
    // 如果没有指定该参数，则子字符串将延续到 stringvar 的最后。
    for (key in o){
	    if(new RegExp("(" + key + ")").test(format)){
    		 $1		= RegExp.$1
	    	,value	= String(o[key])
	    	,value	= $1.length == 1 ? value : ("00" + value).substr(value.length)
    		,format = format.replace($1, value);
	    }
    } 
    return format;	
}
// -------------------fp js --------------------

/**
 * 预设函数的作用域。
 * @param {Object} scope
 * @return {Function}
 */
Function.prototype.delegateScope = function (scope){
	var fn = this;
    return function(){
        return fn.apply(scope, arguments);
    }
}
Function.prototype.map = function(arr, scope){
	var fn = this, fixArgs = $$.fixArgs;
	return function(){
		for(var i = 0, j = arr.length, result = new Array(j); i < j; i++){
			result[i] = fn.apply(scope || this, fixArgs(arguments).concat(arr[i]));
		}
		return result;
	}
}

/**
 * fn与参数的关系。
 */
Function.prototype.reduce = function(init, arr){
	for(var i = 0, j = arr.length; i < j; i++){
		init = this.apply(init, arr[i]);
	}
	return init;
}
$$.reduce = function(scope, fn, arr){
	for(var i = 0, j = arr.length; i < j; i++){
		scope = fn.apply(scope, arr[i]);
	}
	return scope;
}

// forece Args！ so there's no args, it server as Args!
Function.prototype.delegate = function(){
    var 
	     fn 	 = this
	    ,args	 = arguments
	    ,oldArgs = arguments
	    ,aLength = arguments.length;

    var delegateFn = function(){
        var Length, bLength = arguments.length;
        Length = (aLength > bLength) ? aLength : bLength;
        
        for(var i = 0; i < Length; i++){
	    	if(arguments[i])args[i] = arguments[i];
        }   
        // arguments作为数字来使用还是有问题，就是length不能自动更新。
        // ver MS jscript
        // 修正如下:
        args.length = Length;// force
        return fn.apply(this, args);
    };
    return delegateFn;
}
Function.prototype.delegateArgs = function(){
    var 
	     fn 	 = this
	    ,args	 = arguments;
	    
    return function(){
        return fn.apply(arguments[0] || this, args);
    };
}
Function.prototype.delegateO = function(override){
    var fn = this, args = arguments, aLength = arguments.length;

    var delegateFn = function(){
        var Length, bLength = arguments.length;
        Length = (aLength > bLength) ? aLength : bLength;
        
        for(var i = 0; i < Length; i++){
	    	args[i] = arguments[i];
        }   
        // arguments作为数字来使用还是有问题，就是length不能自动更新。
        // ver MS jscript
        // 修正如下:
        args.length = Length;// force
        return fn.apply(this, args);
    };
    return delegateFn;
}

Function.prototype.C = Function.prototype.compose = function(returnToFnAsArg){
    var self = this;
    function ByArgs(argObj, argArr){
    	for(var i in argObj){
    		argArr[i] = argObj[i];
    	}
    }

    return function (){
        var _return = self.apply(this, arguments);
		if(this.skip){
			return _return;
		}
        var isArr   = _return && (typeof _return.pop != 'undefined') && (typeof _return.pop != 'unknown');
        var _arr = isArr ? _return : [_return];
        return returnToFnAsArg.apply(this, _arr);// : returnToFnAsArg(_return);
    };
}

Function.prototype.B = Function.prototype.sequence = function(returnToFnAsArg){
    var self = this;

    return function (){
        var _return = returnToFnAsArg.apply(this, arguments);
        var isArr   = _return && (typeof _return.pop != 'undefined') && (typeof _return.pop != 'unknown');
        
        return self.apply(this, isArr ? _return : [_return]);// : returnToFnAsArg(_return);
    };
} 

/**
 * 两个函数都送入相同的参数。
 * 注意比较下面的代码
    function(){
        var json = arguments[1];
        
        if(qy.isQY_Fields){
            write.apply(this, [ arguments[0], qy(json), arguments[2] ]);
        }else{
            write.apply(this, arguments);
        }
    }
 */
Function.prototype.split = function(_okFn, _failFn){
    var fn = this;
    function cb(){
        if(fn.apply(this, arguments) === true){
            return _okFn.apply(this, arguments);
        }else if(_failFn && typeof (_failFn) == "function"){
            return _failFn.apply(this, arguments);
        }
    }
    return cb;
};

// 修改function的参数
Function.prototype.P = Function.prototype.prefilterAt = function(argIndex, filterFn, isA){
    var selfFn = this;
    return function(){
        var args = Array.prototype.slice.call(arguments, 0);
        args[argIndex] = filterFn.apply(this, isA? arguments : [ arguments[argIndex] ]);
        return selfFn.apply(this, args);
    };
}

Function.prototype.until = function(loopFn, scope) {
    var selfFn = this;
    // selfFn是判断条件
    return function(){
        var result;
        while(!selfFn.apply(scope || this, arguments)){
            result = loopFn.apply(scope || this, arguments);
        }
        return result;
    };
}

/**
 * @class $$.Template
 */
$$.Template = function(tpl, data){
	this.for_Block = /<tpl\b\s+for=".*"[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/g;
	this.html = this.for_Block.test(tpl) ? this.masterTpl(tpl, data) : this.replaceVars(tpl, data);
}
$$.Template.prototype = {
	 getTpl	   : /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/

//	,for_Block : /<tpl\b\s+for=".*"[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/g
	,forWhat   : /<tpl\b[^>]*?for="(.*?)"/ /* Why NOT tpl? /^<tpl\b[^>]*?for="(.*?)"/ */
	,masterTpl : function(tpl, data){
		var 
			 getWhat 	 = this.forWhat
			,_self		 = this 
			,replaceVars = this.replaceVars;
			
		tpl = tpl.replace(this.for_Block, function(outBlock, block){
			var 
				 forWhat = outBlock.match(getWhat)[1]
				,forWhat = data[forWhat]; // 从父对象上得到当前对象，即for="……"，可以是Array或sub-Hash
				
			if(forWhat && forWhat.pop){
				// for Array
				var str = "";
				for(var i = forWhat.length - 1; i >=0; --i){
					str += _self.replaceVars(block, forWhat[i], data);
				}	
				return str;
			}else if(forWhat){
//				debugger;
				// for sub-Hash
				return _self.replaceVars(block, forWhat, data);
			}else if(!forWhat){
				// maybe for Non-Condition
				return '';
			}
		});
		return this.replaceVars(tpl, data);
	}
	,codeVar : /\{\[((?:\\\]|.|\n)*?)\]\}/g
	,getVar  : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g
	// 替换函数
	,replaceVars : function(tpl, data, parentData){
		// 条件判断。
		if(this.getIF_Block.test(tpl)){
			tpl = this.replaceIF.apply(this, arguments); // 完全传递参数，无副作用。
		}	
			
		// 父对象替换。	
		if(this.specVar.test(tpl)){
			tpl = this.replaceParent.apply(this, arguments); // 完全传递参数，无副作用。
		}	
		
		// 普通替换。
		var execBlock = this.exec;
		tpl = tpl.replace(this.codeVar, function(a, b, c){
			with(data){
				return eval(b);
			}
		});
		return tpl.replace(this.getVar, function(a, b, c){
			return data[b];
		});
	}
	
	/**
	 * 我也觉得是一样，.匹配非\n的任何字符，也就是[^\n]，和\n是互逆关系，\s\S本质就是互逆关系，所以应该是一样的，匹配任何字符，或许有高手或经验者可以说出一些例外出来。
	 */
	,getIF_Block : /<tpl\b\s+if="([\w]+)"[^>]*>([\s\S]*)<\/tpl>/
	
	/**
	 * system bug：不能够分组if="(.*)"，否则会报“内存不够”，应该规避之。
	 */
	,getIF_Block : /<tpl\b\s+if=".*"[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/g
	,ifRe	 	 : /<tpl\b[^>]*?if="(.*?)"/
	
	/**
	 * 解析条件判断的<tpl if="...">...</tpl>字元。
	 * @param {String} tpl
	 * @param {Object} data
	 * @param {OBject} parentData
	 * @return {String}
	 */
	,replaceIF: function(tpl, data, parentData){
		var
			 fnTpl	= 'with(data){ return !!({0});};'
			,ifRe  	= this.ifRe
			// tips: replace()可代替写while(){}循环。
			,tpl	= tpl.replace(this.getIF_Block, function(outBlock, block){
				var 
					 fn  = fnTpl.format(outBlock.match(ifRe)[1])
					,fn  = new Function('data', fn);
					
				// 条件判断后结果为true，去掉<tpl if="...">和</tpl>即可。
				// 条件判断后结果为false，则去掉这一块的模板。
				return fn(data) ? block : '';
			});
		return tpl;
	}
	
	,specVar : /{(parent)\.(\w+)}/g
	
	/**
	 * 解析访问父对象成员的{parenet.XXX}字元。
	 * @param {String} tpl
	 * @param {Object} data
	 * @param {OBject} parentData
	 * @return {String}
	 */
	,replaceParent: function(tpl, data, parentData){
		var 
			 specVar = this.specVar
			,data	 = parentData || data // 父对象优先
			,arr;
			
		while((arr = specVar.exec(tpl)) != null){
			if(arr && arr[1] == 'parent'){
				tpl = tpl.replace(specVar, data[arr[2]]);
			}else{
				throw '模板字元有错误！';
			}
		}
		return tpl;
	}
};

$$.JSON = function(){}
$$.JSON.prototype = {
	 toObject: function() {
		return eval("(" + arguments[0] + ')'); 
	}
	,toJSON: function(object) {
	    var type = typeof object;
	    if (type == 'object') {
	        if (Array == object.constructor)
	            type = 'array';
	        else if (object.getFullYear)
	            type = 'date';
	        else if (RegExp == object.constructor)
	            type = 'regexp';
	        else
	            type = 'object';
	           
	    }
	
	    switch (type) {
	        case 'undefined':
	        case 'unknown':
	        case 'function':
	        case 'regexp':
	            return;
	        break;
	        case 'boolean':
	        case 'number':
	            return object.toString();
	        break;
	        case 'date':
	        	 // @todo: user format()
	        	return object.getUTCFullYear() + 
	    			'-' + 
	    			this.makeTwoDigits(object.getUTCMonth() + 1) +
		            '-' +
		            this.makeTwoDigits(object.getUTCDate()) +
		            'T' +
		            this.makeTwoDigits(object.getUTCHours()) +
		            ':' +
		            this.makeTwoDigits(object.getUTCMinutes()) +
		            ':' +
		            this.makeTwoDigits(object.getUTCSeconds()) +
		            'Z';
	        break;
//                case 'number':
//                    return isFinite(object) ? object.toString() : 'null';
//                    break;
	        case 'string':
	            return '"' + object.replace(/(\\|\")/g, "\\$1").replace(/\n|\r|\t/g,
			       function() {
			           var a = arguments[0];
			           return (a == '\n') ? '\\n' :
			                       (a == '\r') ? '\\r' :
			                       (a == '\t') ? '\\t' : ""
			       }) + '"';
	            break;
	        case 'object':
	            if (object === null) return 'null';
	            var results = [];
	            for (var property in object) {
	                var value = arguments.callee(object[property]);
	                if (value !== undefined)
	                    results.push(arguments.callee(property) + ':' + value);
	            }
	            return '{' + results.join(',') + '}';
	        break;
	        case 'array':
	            var results = [];
	            for (var i = 0; i < object.length; i++) {
	                var value = arguments.callee(object[i]);
	                if (value !== undefined) results.push(value);
	            }
	            return '[' + results.join(',') + ']';
	    }
	}
    ,makeTwoDigits: function (n){
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }
};